package com.kusen.funcation;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 1.函数式接口代码
 * jdk1.8自带的4中函数式接口：
 * 1.Consumer<T>：消费型接口（给定一个参数，没有返回值）
 * 2.Supplier<T>：供给型接口 （给定一个参数，有返回值）
 * 3.Function<T, R>：函数型接口（给定一个参数，返回一个参数）
 * 4.Predicate<T>：断言型接口（接受一个参数，返回一个boolean类型的值）
 */
public class FuncationTest {

    public static void main(String[] args) {

        System.out.println("==============消费型函数开始=========================");
        //消费型，使用匿名类实现
        Consumer<List<Integer>> consumer = new Consumer<List<Integer>>() {
            @Override
            public void accept(List<Integer> integers) {
                integers.forEach(System.out::print);
            }
        };
        consumer.accept(Arrays.asList(1, 2, 3, 4, 5, 6));
        System.out.println("");

        //使用Lambda实现
        Consumer<List<Integer>> sons = t -> t.forEach(x -> System.out.println("我是消费型接口Lambda表达式输出：" + x));
        sons.accept(Arrays.asList(10, 20, 30, 40, 50, 60));
        Consumer<Double> con = t -> {
            System.out.println(t);
        };
        happy(100, con);

        System.out.println("===================消费型函数结束======================\n\n\n");
        System.out.println("===================供给型函数开始======================");
        //使用匿名内部类实现
        Supplier<String> sup = new Supplier<String>() {
            @Override
            public String get() {
                //产生一个随机id
                Util util = new Util();
                return util.nextId() + "";
            }
        };
        System.out.println("供给型函数:" + sup.get());

        //使用lambda实现
        Supplier<String> sups = () -> {
            return new Util().nextId() + "";
        };
        System.out.println("供给型函数lambda实现:" + sups.get());
        System.out.println("调用方法实现：" + happ(sups));

        System.out.println("===================供给型函数结束======================\n\n\n");
        System.out.println("===================函数型接口开始======================");
        //匿名内部类实现 给定一个Integer类型的数据返回一个String类型的数据
        Function<Integer, String> fun = new Function<Integer, String>() {
            @Override
            public String apply(Integer s) {
                StringBuffer sb = new StringBuffer("我是函数式接口生成的数据：");
                for (int i = 1; i < s; i++) sb.append(i).append("\t");
                return sb.toString();
            }
        };
        System.out.println(fun.apply(10));
        //lambda实现
        Function<Integer, String> funs = i -> {
            StringBuffer sb = new StringBuffer("我是函数式接口lambda实现：");
            for (int j = 0; j < i; j++) sb.append(j).append("\t");
            return sb.toString();
        };
        System.out.println(funs.apply(20));
        System.out.println("===================函数型接口结束======================\n\n\n");
        System.out.println("===================断言型函数接口开始======================");
        //匿名内部类实现
        Predicate<List<Object>> pre = new Predicate<List<Object>>() {
            @Override
            public boolean test(List<Object> list) {
                return list.isEmpty();
            }
        };
        System.out.println("匿名内部类实现断言型函数接口" + pre.test(Arrays.asList()));
        System.out.println("匿名内部类实现断言型函数接口" + pre.test(Arrays.asList(1, 2, 3, 4)));
        //使用Lambda实现
        Predicate<String> pres = t -> {
            return t.equals("hopeful");
        };
        System.out.println("lambda实现断言型函数接口" + pres.test("hopeful"));
        System.out.println("lambda实现断言型函数接口" + pres.test("hopefulPlus"));
        System.out.println("===================断言型函数接口结束======================");
        System.out.println("我是改变的地方");
    }

    //消费型接口方法入参使用
    public static void happy(double money, Consumer<Double> con) {
        con.accept(money);
    }

    //供给型接口方法入参使用
    public static String happ(Supplier<String> con) {
        return con.get();
    }

    //生成id的生成器类
    static class Util {
        // 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动）
        private final static long twepoch = 1288834974657L;
        // 机器标识位数
        private final static long workerIdBits = 5L;
        // 数据中心标识位数
        private final static long datacenterIdBits = 5L;
        // 机器ID最大值
        private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
        // 数据中心ID最大值
        private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        // 毫秒内自增位
        private final static long sequenceBits = 12L;
        // 机器ID偏左移12位
        private final static long workerIdShift = sequenceBits;
        // 数据中心ID左移17位
        private final static long datacenterIdShift = sequenceBits + workerIdBits;
        // 时间毫秒左移22位
        private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

        private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
        /* 上次生产id时间戳 */
        private long lastTimestamp = -1L;
        // 0，并发控制
        private long sequence = 0L;

        private long workerId;
        // 数据标识id部分
        private long datacenterId;
        //老ID
        private long oldId;

        public Util() {
            this.datacenterId = getDatacenterId(maxDatacenterId);
            this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
        }

        /**
         * @param workerId     工作机器ID
         * @param datacenterId 序列号
         */
        public Util(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        /**
         * 获取下一个ID
         *
         * @return
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            if (lastTimestamp == timestamp) {
                // 当前毫秒内，则+1
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    // 当前毫秒内计数满了，则等待下一秒
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0L;
            }

            lastTimestamp = timestamp;
            // ID偏移组合生成最终的ID，并返回ID
            long nextId = ((timestamp - twepoch) << timestampLeftShift)
                    | (datacenterId << datacenterIdShift)
                    | (workerId << workerIdShift) | sequence;
            if (oldId == nextId) {
                nextId = nextId + 1;
            }
            oldId = nextId;
            return nextId;
        }

        private long tilNextMillis(final long lastTimestamp) {
            long timestamp = this.timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = this.timeGen();
            }
            return timestamp;
        }

        private long timeGen() {
            return System.currentTimeMillis();
        }

        /**
         * <p>
         * 获取 maxWorkerId
         * </p>
         */
        protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
            StringBuffer mpid = new StringBuffer();
            mpid.append(datacenterId);
            String name = ManagementFactory.getRuntimeMXBean().getName();
            if (!name.isEmpty()) {
                /*
                 * GET jvmPid
                 */
                mpid.append(name.split("@")[0]);
            }
            /*
             * MAC + PID 的 hashcode 获取16个低位
             */
            return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
        }

        /**
         * <p>
         * 数据标识id部分
         * </p>
         */
        protected long getDatacenterId(long maxDatacenterId) {
            long id = 0L;
            try {
                InetAddress ip = InetAddress.getLocalHost();
                NetworkInterface network = NetworkInterface.getByInetAddress(ip);
                if (network == null) {
                    id = 1L;
                } else {
                    byte[] mac = network.getHardwareAddress();
                    id = ((0x000000FF & (long) mac[mac.length - 1])
                            | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            } catch (Exception e) {
                System.out.println(" getDatacenterId: " + e.getMessage());
            }
            return id;

        }
    }
}
